package com.kuang.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

// 懒汉模式实现单例，反射不安全测试
public class LazyManReflectTest {

    //public LazyManReflectTest() {
    //    System.out.println(Thread.currentThread().getName() + "ok");
    //}

    // 构造器加锁：这样多线程通过类获取实例时，会先判断下，如果实例已存在，就抛出异常，从而实现单例
    //public LazyManReflectTest() {
    //    synchronized (LazyManReflectTest.class){
    //        if (lazyMan != null){
    //            throw new RuntimeException("不要试图使用反射破坏单例");
    //        }
    //    }
    //}

    // 标志位
    private static boolean song = false;
    // 构造器加锁+标志位  避免通过构造器创建2个实例时，获取到不同实例，实现单例模式
    public LazyManReflectTest() {
        synchronized (LazyManReflectTest.class){
            if (!song){
                song = true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏单例");
            }
        }
    }

    // 双重检测锁模式：禁止指令重排，确保原子性操作
    private volatile static LazyManReflectTest lazyMan;
    public static LazyManReflectTest getInstance(){
        if (lazyMan == null){
            synchronized (LazyMan.class){
                if (lazyMan == null){
                    lazyMan = new LazyManReflectTest();
                }
            }
        }
        return lazyMan;
    }

    // 反射破坏单例测试
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        // 通过反射获取1个实例、通过构造器获取1个实例，判断是否单例
        //LazyManReflectTest instance1 = LazyManReflectTest.getInstance();
        //Constructor<LazyManReflectTest> declaredConstructor = LazyManReflectTest.class.getDeclaredConstructor(null);
        //declaredConstructor.setAccessible(true);
        //LazyManReflectTest instance2 = declaredConstructor.newInstance();
        //LazyManReflectTest instance3 = declaredConstructor.newInstance(); // 通过构造器获取2个实例，判断是否单例

        // 通过反射获取到的实例和构造器获取到实例不是一个，获取到2个实例，破坏了单例模式
        // 解决办法：1.构造器锁住，构造的时候判断下，是否为空，如果存在了，就抛出异常  reflect.InvocationTargetException 不要试图使用反射破坏单例
        // 通过构造器获取2个实例，单例被破坏
        // 解决办法：通过一个标志位，默认false，通过反射newInstance后，设置为true，这时候再次通过反射 newInstance时候，抛出异常
        //System.out.println(instance1); //com.kuang.single.LazyManReflectTest@7f31245a
        //System.out.println(instance2);  //com.kuang.single.LazyManReflectTest@6d6f6e28
        //System.out.println(instance3);  //com.kuang.single.LazyManReflectTest@135fbaa4   单例被破坏


        // 拿到标志位字段，破坏私有权限，把标志位的值又改为 false, 单例又就被破坏了
        Field song = LazyManReflectTest.class.getDeclaredField("song");
        song.setAccessible(true); //设置属性为可访问
        Constructor<LazyManReflectTest> constructor = LazyManReflectTest.class.getDeclaredConstructor();
        constructor.setAccessible(true);  //设置构造器为可访问
        LazyManReflectTest newInstance1 = constructor.newInstance();
        song.set(newInstance1,false); //将标志位设为未生成实例时的false
        LazyManReflectTest newInstance2 = constructor.newInstance();
        System.out.println(newInstance1);  //com.kuang.single.LazyManReflectTest@6d6f6e28
        System.out.println(newInstance2);  //com.kuang.single.LazyManReflectTest@135fbaa4 单例被破坏

        // 那这个如何解决？ 源码  xxx.newInstance() 进去，如果类是一个枚举类型，就不能通过反射创建对象！
    }
}
